// **********************************************************************
//                            Project Nitro
//                        ---------------------
//
//  File        : Level.cpp
//  Description : Level
//  Author      : Guillaume Delhumeau <guillaume.delhumeau@gmail.com>
//  License     : GNU General Public License 3
//
// **********************************************************************

#include <ClanLib/core.h>
#include "Level.h"
#include "Tools.h"
#include "Tile.h"
#include "MapTile.h"
#include "MapElement.h"
#include "Memory.h"

/************************************************************************/
/* Constructor                                                          */
/************************************************************************/
Level::Level()
{
	_nb_cols = 0;
	_nb_rows = 0;
}

/************************************************************************/
/* Destructor                                                           */
/************************************************************************/
Level::~Level()
{
	_unload();
}

/************************************************************************/
/* Load level file                                                      */
/************************************************************************/
void Level::load_level_file(std::string filename, Screen & screen)
{
	// Unload level if it was already loaded
	_unload();
	
	// Making virtual file systems
	CL_VirtualFileSystem vfs(filename, true);
	CL_VirtualDirectory vd(vfs, "./");	

	// Load XML
	CL_IODevice file = vd.open_file_read("level.xml");
	CL_DomDocument document(file);
	CL_DomElement root = document.get_document_element();

	// Tiles
	CL_DomElement tiles = root.named_item("tiles").to_element();
	_load_tiles(root.named_item("tiles").to_element(), screen, vd);

	// Map
	CL_DomElement map_elem = root.named_item("map").to_element();
	_load_map(map_elem);

}

/************************************************************************/
/* Load <tiles> tag                                                     */
/************************************************************************/
void Level::_load_tiles(CL_DomElement root, Screen & screen, CL_VirtualDirectory & vd)
{
	// Tiles propreties
	_tiles_width = root.get_attribute_int("width");
	_tiles_height = root.get_attribute_int("height");

	// Tiles list
	CL_DomNode cur = root.get_first_child();
	while (cur.is_element())
	{
		if (cur.get_node_name() == "tile")
		{
			CL_DomElement element = cur.to_element();

			Tile * p_tile = my_new Tile();
			_tiles.push_back(p_tile);
			p_tile->load_from_xml_node(element, screen, vd);
			my_assert(p_tile->get_id() == (int)_tiles.size()-1, "Error with ID");
		}
		cur = cur.get_next_sibling();
	}
}

/************************************************************************/
/* Load <map> tag                                                       */
/************************************************************************/
void Level::_load_map(CL_DomElement root)
{
	_nb_cols = root.get_attribute_int("nbCols");
	_nb_rows = root.get_attribute_int("nbRows");

	int nb_tiles = _nb_cols * _nb_rows;
	for(int i=0; i<nb_tiles; ++i)
	{
		_map.push_back(my_new MapElement());
	}
		
	CL_DomNode cur = root.get_first_child();
	while (cur.is_element())
	{
		if (cur.get_node_name() == "mapTile")
		{
			CL_DomElement element = cur.to_element();

			Tile * p_tile = _get_tile_from_id(element.get_attribute("tile"));
			CL_String position = element.get_attribute("pos");
			CL_StringHelp string_help;
			std::vector<CL_TempString> v = string_help.split_text(position, ",");
			int x = string_help.text_to_int(v.at(0));
			int y = string_help.text_to_int(v.at(1));

			int rotation = element.get_attribute_int("rotation", 0);
			MapElement & map_element = get_map_tile(x, y);
			MapTile map_tile(p_tile, (float)rotation);
			map_element.add_ground(map_tile);
		}
		cur = cur.get_next_sibling();
	}
}

/************************************************************************/
/* Get tile from id                                                     */
/************************************************************************/
Tile* Level::_get_tile_from_id(CL_String id)
{
	CL_StringHelp string_help;
	id = id.substr(id.find_first_of("_")+1, id.size());
	int i_id = string_help.text_to_int(id);	
	return _tiles[i_id];
}

/************************************************************************/
/* Draw level                                                           */
/************************************************************************/
void Level::draw(Screen & screen, float scroll_x, float scroll_y)
{
	int begin_col = (int) (scroll_x / _tiles_width);
	int begin_row = (int) (scroll_y / _tiles_height);
	int pixel_x = (int)scroll_x % _tiles_width;
	int pixel_y = (int)scroll_y % _tiles_height;
	int end_col = screen.get_gc().get_width() / _tiles_width + begin_col + 2;
	int end_row = screen.get_gc().get_height() / _tiles_height + begin_row + 2;
	if(end_col > _nb_cols)
		end_col = _nb_cols;
	if(end_row > _nb_rows)
		end_row = _nb_rows;
	for(int col=begin_col; col<end_col; ++col)
	{
		for(int row=begin_row; row<end_row; ++row)
		{
			MapElement & map_tile = get_map_tile(col, row);
			float x = (float)(col-begin_col)*_tiles_width - pixel_x;
			float y = (float)(row-begin_row)*_tiles_height - pixel_y;
			map_tile.draw_ground(screen, x, y);
		}
	}
}

/************************************************************************/
/* Unload resources                                                     */
/************************************************************************/
void Level::_unload()
{
	int nb_elements = _nb_cols * _nb_rows;
	for(int i=0; i<nb_elements; ++i)
	{
		my_delete(_map[i]);
	}
	_map.clear();
	
	_nb_cols = 0;
	_nb_rows = 0;
	std::vector<Tile*>::iterator it_tile = _tiles.begin();
	while(it_tile != _tiles.end())
	{
		my_delete(*it_tile);
		it_tile = _tiles.erase(it_tile);
	}
	_tiles.clear();
}
